package com.luckyframe.project.jmeter.consumer;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.luckyframe.project.jmeter.execution.ResultDataParse;
import com.luckyframe.project.jmeter.jmeterTestPlanMetric.domain.JmeterTestPlanMetric;
import com.luckyframe.project.jmeter.report.domain.Chart;
import com.luckyframe.project.jmeter.report.domain.Line;
import org.apache.commons.collections.CollectionUtils;
import org.apache.jmeter.report.core.DataContext;
import org.apache.jmeter.report.core.Sample;
import org.apache.jmeter.report.dashboard.JsonizerVisitor;
import org.apache.jmeter.report.processor.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static com.luckyframe.project.jmeter.util.DoubleUtil.round;

public class SampleHandler {

    private static final Logger logger = LoggerFactory.getLogger(SampleHandler.class);

    private List<AbstractSampleConsumer> graphConsumerList = ResultDataParse.graphConsumerList();

    private List<AbstractSampleConsumer> caseWatchConsumer = ResultDataParse.caseWatchList();

    private List<AbstractSampleConsumer> planWatchConsumer = ResultDataParse.planWatchList();


    public DataContext generate(List<Sample> samples, List<AbstractSampleConsumer> consumerList) {

        if(CollectionUtils.isEmpty(consumerList)){
            return new DataContext();
        }

        Map<String, SampleContext> resultSampleContext = generateSampleContext(samples, consumerList);

        DataContext dataContext=new DataContext();

        JsonizerVisitor jsonizerVisitor=new JsonizerVisitor();

        resultSampleContext.forEach((name,sampleContext)->{
            Map<String, Object> data = sampleContext.getData();
            addResultToContext(name,data,dataContext,jsonizerVisitor);
        });
        return dataContext;
    }

    private Map<String, SampleContext> generateSampleContext(List<Sample> samples, List<AbstractSampleConsumer> consumerList) {
        Map<String, SampleContext> resultSampleContext = new HashMap<>();

        consumerList.forEach(consumer -> {
            SampleContext sampleContext = new SampleContext();
            consumer.setSampleContext(sampleContext);
            consumer.startConsuming();
            resultSampleContext.put(consumer.getName(), sampleContext);
        });

        if(CollectionUtils.isNotEmpty(samples)){
            for (Sample sample : samples) {
                consumerList.forEach(consumer -> consumer.consume(sample, 0));
            }
        }

        consumerList.forEach(SampleConsumer::stopConsuming);
        return resultSampleContext;
    }


    public DataContext generateGraphData(List<Sample> samples) {
        return generate(samples,graphConsumerList);
    }


    public DataContext generateCaseStatistics(List<Sample> samples) {
        return generate(samples,caseWatchConsumer);
    }


    public DataContext generatePlanStatistics(List<Sample> samples) {
        return generate(samples,planWatchConsumer);
    }

    public List<Chart> generateCharts(List<Sample> samples) {

        Map<String, SampleContext> sampleContextMap = generateSampleContext(samples, graphConsumerList);

        List<Chart> charts=new ArrayList<>();

        sampleContextMap.forEach((name,sampleContext)->{

            Map<String, Object> map = sampleContext.getData();

            for (String key : map.keySet()) {

                Chart chart=new Chart(key);
                charts.add(chart);

                MapResultData mapResultData = (MapResultData) map.get(key);
                ListResultData series = (ListResultData) mapResultData.getResult("series");

                if (series.getSize() > 0) {

                    List<Line> lines=new ArrayList<>();
                    chart.setLines(lines);

                    for (int j = 0; j < series.getSize(); j++) {

                        MapResultData resultData = (MapResultData) series.get(j);
                        ValueResultData label = (ValueResultData) resultData.getResult("label");

                        Line line=new Line(label.getValue().toString());
                        lines.add(line);

                        ListResultData data = (ListResultData) resultData.getResult("data");

                        if (data.getSize() > 0) {

                            for (int i = 0; i < data.getSize(); i++) {
                                ListResultData pointData = (ListResultData) data.get(i);
                                line.addPoint(toPoint(pointData));

                            }
                        }
                    }
                }
            }
        });


        for (Chart chart : charts) {

            List<Line> lines = chart.getLines();
            if(CollectionUtils.isEmpty(lines)){
                continue;
            }

            double minX=0;
            double minY=0;
            double maxX=0;
            double maxY=0;

            for (Line line : lines) {

                List<Double[]> points = line.getPoints();
                if(CollectionUtils.isEmpty(points)){
                    continue;
                }

                for (Double[] point : points) {
                    minX=Math.min(point[0],minX);
                    minY=Math.min(point[1],minY);
                    maxX=Math.max(point[0],maxX);
                    maxY=Math.max(point[1],maxY);
                }

            }

            chart.setMinX(minX);
            chart.setMinY(minY);
            chart.setMaxX(maxX);
            chart.setMaxY(maxY);
        }


        return charts;

    }



    public JmeterTestPlanMetric generatePlanMetric(List<Sample> samples) {


        DataContext statisticsData = generate(samples, planWatchConsumer);
        String statisticsString = Objects.toString(statisticsData.get("overallStatistics"),"{}");

        try {

            JSONObject jsonObject = JSON.parseObject(statisticsString);

            JSONArray items = jsonObject.getJSONArray("items");

            if(items.size()>0){

                JmeterTestPlanMetric metric=new JmeterTestPlanMetric();

                JSONArray data = items.getJSONObject(0).getJSONArray("data");

                Double tps = data.getDouble(10);
                Double avgResp = data.getDouble(4);
                Double errorRatio = data.getDouble(3);
                Double hits = data.getDouble(13);

                metric.setTps(round(tps,2,1 ));
                metric.setAvgRespTime(round(avgResp,2,1 ));
                metric.setErrorRatio(round(errorRatio,2,1 ));
                metric.setHits(round(hits,2,1 ));

                return metric;

            }

        }catch (Exception ex){
            //
            logger.error("statisticsString parse error",ex);
            throw new RuntimeException(ex);
        }


        return null;

    }


    private  Double[] toPoint(ListResultData listResultData) {

        ValueResultData xval = (ValueResultData)listResultData.get(0);
        ValueResultData yval = (ValueResultData)listResultData.get(1);

        Double x=Double.valueOf(xval.getValue().toString());
        Double y=Double.valueOf(yval.getValue().toString());


        return new Double[]{round(x),round(y)};

    }


    private   <T> void addResultToContext(
            String resultKey, Map<String, Object> storage, DataContext dataContext,
            ResultDataVisitor<T> visitor) {

        Object data = storage.get(resultKey);
        if (data instanceof ResultData) {
            ResultData result = (ResultData) data;

            dataContext.put(resultKey, result.accept(visitor));
        }
    }


    public static void main(String[] args) {

        System.out.println(round(0.01));


    }


}
